/*
 * Copyright (c) 2020-2021 imlzw@vip.qq.com jweb.cc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cc.jweb.adai.web.system.generator.service;

import cc.jweb.adai.web.system.generator.model.FieldModel;
import cc.jweb.adai.web.system.generator.model.GeneratorModel;
import cc.jweb.adai.web.system.generator.model.TableModel;
import cc.jweb.adai.web.system.generator.model.TemplateModel;
import cc.jweb.adai.web.system.generator.model.code.FieldCodeModel;
import cc.jweb.adai.web.system.generator.model.code.GeneratorConfig;
import cc.jweb.adai.web.system.generator.model.code.TableCodeModel;
import cc.jweb.adai.web.system.generator.utils.GeneratorUtils;
import cc.jweb.adai.web.system.generator.utils.ModelUtils;
import cc.jweb.adai.web.websocket.service.LogWebSocketService;
import cc.jweb.boot.utils.file.FileUtils;
import cc.jweb.boot.utils.lang.HumpNameUtils;
import cc.jweb.boot.web.render.JwebBootRenderFactory;
import cc.jweb.boot.web.resource.WebRootResourceFactory;
import com.jfinal.kit.PathKit;
import com.jfinal.template.Engine;
import com.jfinal.template.Template;
import com.jfinal.template.source.ISource;
import io.jboot.Jboot;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;

/**
 * 代码生成器
 */
public class CodeGenerator {

    private static final Logger logger = LoggerFactory.getLogger(CodeGenerator.class);
    private static String generatorResDir = null;
    private static String modelTemplateDir = generatorResDir + File.separator + "model";
    private static String templateDir = generatorResDir + File.separator + "template";
    private static Engine engine = new Engine();

    public static void init() {
        JwebBootRenderFactory.getInstance().initEngine(engine);
        generatorResDir = Jboot.configValue("jweb.adai.generator.template.dir");
        modelTemplateDir = generatorResDir + File.separator + "model";
        templateDir = generatorResDir + File.separator + "template";
        if (!new File(modelTemplateDir).exists()) {
            try {
                FileUtils.makeDir(modelTemplateDir, true);
                ISource source = WebRootResourceFactory.me().getSource("/WEB-INF/template/model/model.java", "UTF-8");
                StringBuilder content = source.getContent();
                FileUtils.write(modelTemplateDir + "/#(table.modelName).java", content.toString(), Charset.forName("UTF-8"));
            } catch (IOException e) {
                logger.error("写入模型模板内容到" + modelTemplateDir + "目录异常！", e);
            }
        }
    }

    /**
     * 根据模块配置生成模板
     */
    public synchronized static void generator(GeneratorModel generatorModel) throws IOException {
        LogWebSocketService.sendMessage("正在生成代码...");
        GeneratorConfig generatorConfig = generatorModel.toGeneratorConfig();
        TemplateModel template = generatorConfig.getTemplate();
        List<TableCodeModel> tables = generatorConfig.getTables();
        tables = initTablesFromDb(tables);

        // 构建模板渲染参数
        Map<String, Object> params = new HashMap<>();
        params.put("generator", generatorModel);
        params.put("generatorId", generatorModel.getGeneratorId());
        params.put("generatorConfig", generatorConfig);
        params.put("tables", tables);
        params.put("template", template);
        params.put("templateKey", template.getTemplateKey());
        params.put("baseControllerUri", generatorConfig.getCtlUri());
        params.put("basePackageName", generatorConfig.getPackagePath());
        String basePackagePath = GeneratorUtils.generatPackagePath(generatorConfig.getPackagePath());
        params.put("basePackagePath", basePackagePath);
        params.put("currentDatetime", new Date());
        params.put("currentTimeMillis", System.currentTimeMillis());
        for (int i = 0; i < tables.size(); i++) {
            // 单个表模板渲染参数
            TableCodeModel tableModel = tables.get(i);
            List<FieldCodeModel> fieldModels = tableModel.getFieldModelList();
            boolean hasCreateDatetime = false;
            boolean hasModifyDatetime = false;
            boolean hasOrderNo = false;
            for (FieldCodeModel fieldModel : fieldModels) {
                if (fieldModel.getFieldKey().equals("create_datetime")) {
                    hasCreateDatetime = true;
                }
                if (fieldModel.getFieldKey().equals("modify_datetime")) {
                    hasModifyDatetime = true;
                }
                if (fieldModel.getFieldKey().equals("order_no")) {
                    hasOrderNo = true;
                }
                fieldModel.put("fieldConfigObject", fieldModel.toFieldConfig());
            }
            tableModel.put("fields", fieldModels);
            tableModel.put("primaryKey", ModelUtils.findPrimaryKey(fieldModels));
            tableModel.put("primaryField", ModelUtils.findPrimaryField(fieldModels));
            tableModel.put("hasCreateDatetime", hasCreateDatetime);
            tableModel.put("hasModifyDatetime", hasModifyDatetime);
            tableModel.put("hasOrderNo", hasOrderNo);
            tableModel.put("modelName", HumpNameUtils.lineToHump(tableModel.getTableKey(), true));
            tableModel.put("modelVarName", HumpNameUtils.lineToHump(tableModel.getTableKey(), false));
            params.put("table" + (i + 1), tableModel);
        }

        // 定位模板目录
        File templateDir = new File(CodeGenerator.templateDir + File.separator + template.getTemplateId());
        // 定位代码生成输出临时目录
        File targetDir = new File(CodeGenerator.templateDir + File.separator + "output" + File.separator + "generator_" + generatorModel.getGeneratorId());
        // 清空目录
        try {
            System.gc();//启动jvm垃圾回收
            FileUtils.delete(targetDir);
        } catch (Exception exception) {
            exception.printStackTrace();
        }
        engine.setBaseTemplatePath(templateDir.getAbsolutePath());
        // 渲染代码
        renderTemplate(templateDir, targetDir, params);

        // 重新整理生成的文件目录结构
        String codeOutputPath = CodeGenerator.templateDir + File.separator + "output" + File.separator + "generator_" + generatorModel.getGeneratorId() + File.separator + template.getTemplateId();
        File file = new File(codeOutputPath);
        String parent = file.getParent();
        // code path
        String codePath = parent + "/src/main/java/" + generatorConfig.getPackagePath().replace(".", "/");
        // view path
        String viewPath = parent + "/src/main/webapp/WEB-INF/views/" + generatorConfig.getPackagePath().replace(".", "/") + "/gid_" + generatorModel.getGeneratorId();
        FileUtils.makeDir(codePath, true);
        FileUtils.makeDir(viewPath, true);
        FileUtils.move(codeOutputPath, codePath);
        FileUtils.move(codePath + "/views", viewPath);
        FileUtils.delete(codeOutputPath);
    }

    private static List<TableCodeModel> initTablesFromDb(List<TableCodeModel> tables) {
        for (TableCodeModel table : tables) {
            TableModel tableModel = TableModel.dao.findById(table.getTableId());
            if (tableModel != null) {
                initTableCodeModel(tableModel, table);
            }
        }
        return tables;
    }

    private static void initTableCodeModel(TableModel source, TableCodeModel target) {
        target.init(source);
        List<FieldModel> fieldModels = FieldModel.dao.find("select * from sys_field_model where table_id = ?", source.getTableId());
        if (fieldModels != null) {
            for (FieldModel fieldModel : fieldModels) {
                for (FieldCodeModel fieldCodeModel : target.getFieldModelList()) {
                    if (fieldCodeModel.getFieldId().equals(fieldModel.getFieldId())) {
                        fieldCodeModel.init(fieldModel);
                    }
                }
            }
        }
    }


    /**
     * @param tempFile
     * @param targetDir
     */
    private static void renderTemplate(File tempFile, File targetDir, Map params) throws IOException {
        String tempFileName = tempFile.getName();
        if (tempFileName.equals("_include")) {
            return;
        }
        tempFileName = engine.getTemplateByString(tempFileName).renderToString(params);
        if (tempFile.isDirectory()) {
            String dirPath = targetDir.getAbsolutePath() + File.separator + tempFileName;
            FileUtils.makeDir(dirPath, true);
            File[] files = tempFile.listFiles();
            if (files != null) {
                for (File file : files) {
                    renderTemplate(file, new File(dirPath), params);
                }
            }
        } else {
            logger.info("准备渲染模板：" + tempFile.getAbsolutePath());
            LogWebSocketService.sendMessage("准备渲染模板：" + tempFile.getName());
            Template template = engine.getTemplateByString(FileUtils.readText(tempFile.getAbsolutePath(), "\r\n", Charset.forName("UTF-8")));
            String targetFilePath = targetDir + File.separator + tempFileName;
            Writer writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(targetFilePath), "UTF-8"));
            String content = template.renderToString(params);
            writer.write(deleteCRLFOnce(content));
            writer.flush();
            writer.close();
            logger.info("渲染模板完成：" + targetFilePath);
            LogWebSocketService.sendMessage("渲染模板完成：" + tempFileName);
        }
    }

    /**
     * 删除多余的空白行，仅留一行
     *
     * @param input
     * @return
     */
    private static String deleteCRLFOnce(String input) {
        return input.replaceAll("((\r\n)|\n)[\\s\t ]*(\\1)+", "$1\r\n").replaceAll("^((\r\n)|\n)", "");
    }

    /**
     * 根据模块配置生成模板
     */
    public synchronized static void generatorModel(TableModel tableModel, List<? extends FieldModel> fieldModels) throws IOException {

        // 构建模板渲染参数
        Map<String, Object> params = new HashMap<>();
        // 单个表模板渲染参数
        boolean hasCreateDatetime = false;
        for (FieldModel fieldModel : fieldModels) {
            if (fieldModel.getFieldKey().equals("create_datetime")) {
                hasCreateDatetime = true;
            }
            fieldModel.put("fieldConfigObject", fieldModel.toFieldConfig());
        }
        tableModel.put("fields", fieldModels);
        tableModel.put("primaryKey", ModelUtils.findPrimaryKey(fieldModels));
        tableModel.put("hasCreateDatetime", hasCreateDatetime);
        String modelName = HumpNameUtils.lineToHump(tableModel.getTableKey(), true);
        tableModel.put("modelName", modelName);
        tableModel.put("modelVarName", HumpNameUtils.lineToHump(tableModel.getTableKey(), false));
        String basePackagePath = GeneratorUtils.generatPackagePath(tableModel.getModelPackage());
        tableModel.put("packagePath", basePackagePath);

        params.put("currentDatetime", new Date());
        params.put("currentTimeMillis", System.currentTimeMillis());

        params.put("table", tableModel);
        // 定位模板目录
        File templateDir = new File(CodeGenerator.modelTemplateDir);
        // 定位代码生成输出临时目录
        File targetDir = new File(CodeGenerator.generatorResDir + File.separator + "output" + File.separator + "tid_" + tableModel.getTableId());
        // 清空目录
        FileUtils.delete(targetDir);
        // 渲染代码
        renderTemplate(templateDir, targetDir, params);

        // 重新整理生成的文件目录结构
        String modelPath = CodeGenerator.generatorResDir + File.separator + "output" + File.separator + "tid_" + tableModel.getTableId() + File.separator + "model";
        // code path
        String codePath = CodeGenerator.generatorResDir + File.separator + "output" + File.separator + "tid_" + tableModel.getTableId() + "/src/main/java/" + tableModel.getModelPackage().replace(".", "/");
        FileUtils.makeDir(codePath, true);
        FileUtils.move(modelPath, codePath);
        FileUtils.delete(modelPath);

    }

    public static void main(String[] args) throws IOException {
    }

    /**
     * 代码生成输出目录
     *
     * @param generatorModel
     */
    public static String getCodeOutputPath(GeneratorModel generatorModel) {
        GeneratorConfig generatorConfig = generatorModel.toGeneratorConfig();
        TemplateModel template = generatorConfig.getTemplate();
        return CodeGenerator.templateDir + File.separator + "output" + File.separator + "generator_" + generatorModel.getGeneratorId();
    }

    /**
     * 获取模型代码生成输出目录
     *
     * @param tableModel
     */
    public static String getCodeOutputPath(TableModel tableModel) {
        return CodeGenerator.generatorResDir + File.separator + "output" + File.separator + "tid_" + tableModel.getTableId();
    }

    public static void applyModel2LocalDevProject(TableModel tableModel) {

    }

    /**
     * 编译生成的代码
     *
     * @param generatorModel
     */
    public static void compileCode(GeneratorModel generatorModel) {
        LogWebSocketService.sendMessage("准备编译...");
        String codeOutputPath = getCodeOutputPath(generatorModel);
        compileCode(codeOutputPath);
    }

    /**
     * 编译生成的代码
     *
     * @param tableModel
     */
    public static void compileCode(TableModel tableModel) {
        String codeOutputPath = getCodeOutputPath(tableModel);
        compileCode(codeOutputPath);
    }

    private static void compileCode(String codeOutputPath) {
        String javaPath = codeOutputPath + "/src/main/java";
        String projectPath = getProjectPath();
        String devSourcePath = projectPath + File.separator + "src/main/java";
        // 开发IDE时
        if (FileUtils.fileExists(devSourcePath)) {
            LogWebSocketService.sendMessage("监测到IDE开发环境:" + devSourcePath);
            String finalProjectPath = projectPath;
            Runnable compileCodeTask = new Runnable() {
                @Override
                public void run() {
                    String targetPath = codeOutputPath + "/target/classes";
                    String classPathParams = getClassPathParams();
                    StringBuilder sourceFiles = generatorCompileSourceFiles(new File(javaPath));
                    FileUtils.makeDir(targetPath, true);
                    // 拷贝资源文件
                    try {
                        FileUtils.copy(javaPath, targetPath);
                    } catch (IOException e) {
                        logger.error("拷贝资源文件异常！", e);
                        LogWebSocketService.sendError(e);
                    }
                    LogWebSocketService.sendMessage("正在编译...");
                    String cmd = "javac " + classPathParams + " -verbose -encoding UTF-8 -d " + targetPath + " " + sourceFiles;
                    cmd(cmd, finalProjectPath);
                }
            };

            // 判断是否复制lib依赖库
            String copyLibPomXmlPath = projectPath + File.separator + "src/main/resources/pom.copy.lib.xml";
            String jwebAdaiVersion = getJwebAdaiVersion(projectPath + File.separator + "/pom.xml");
            String jwebAdaiVersion4copy = getJwebAdaiVersion(copyLibPomXmlPath);
            String libPath = projectPath + "/target/lib";
            boolean isLibExist = FileUtils.fileExists(libPath);
            if (jwebAdaiVersion != null && (!jwebAdaiVersion.equals(jwebAdaiVersion4copy) || !isLibExist)) {
                if (!isLibExist) {
                    LogWebSocketService.sendMessage("未发现编译依赖库[" + projectPath + "/target/lib], 准备生成...");
                }
                if (!jwebAdaiVersion.equals(jwebAdaiVersion4copy)) {
                    if (isLibExist) {
                        // 清除旧的依赖库
                        FileUtils.delete(libPath);
                    }
                    LogWebSocketService.sendMessage("生成jweb-adai-" + jwebAdaiVersion + "的相关依赖库...");
                    try {
                        refreshCopyLibPomXmlFile(copyLibPomXmlPath, jwebAdaiVersion);
                    } catch (IOException e) {
                        LogWebSocketService.sendMessage("刷新src/main/resources/pom.copy.lib.xml文件内容异常！" + e.getMessage());
                    }
                }
                // 生成编译 依赖库
                String cmd = "cmd /C \"mvn validate -f src/main/resources/pom.copy.lib.xml\"";
                cmd(cmd, projectPath, compileCodeTask);
            } else {
                compileCodeTask.run();
            }

        }
        // 部署时
        else {
            String targetPath = codeOutputPath + "/target/classes";
            String classPathParams = getClassPathParams();
            StringBuilder sourceFiles = generatorCompileSourceFiles(new File(javaPath));
            FileUtils.makeDir(targetPath, true);
            // 拷贝资源文件
            try {
                FileUtils.copy(javaPath, targetPath);
            } catch (IOException e) {
                logger.error("拷贝资源文件异常！", e);
                LogWebSocketService.sendError(e);
            }
            LogWebSocketService.sendMessage("正在编译...");
            cmd("javac " + classPathParams + " -verbose -encoding UTF-8 -d " + targetPath + " " + sourceFiles, projectPath);

        }
    }

    /**
     * 刷新pom.copy.lib.cml文件内容的版本号
     */
    private static void refreshCopyLibPomXmlFile(String copyLibPomXmlPath, String newJwebAdaiVersion) throws IOException {
        ISource source = WebRootResourceFactory.me().getSource(null, "/WEB-INF/template/pom.copy.lib.xml", "UTF-8");
        StringBuilder content = source.getContent();
        FileUtils.write(copyLibPomXmlPath, content.toString().replace("#(jwebAdaiVersion)", newJwebAdaiVersion), Charset.forName("UTF-8"));
    }

    /**
     * 获取pom.xml文件中 jweb-adai的版本号
     *
     * @param pomXmlFile
     * @return
     * @throws IOException
     */
    private static String getJwebAdaiVersion(String pomXmlFile) {
        if (!new File(pomXmlFile).exists()) {
            return null;
        }
        try {
            String pomContent = FileUtils.readText(pomXmlFile, "", Charset.forName("UTF-8"));
            pomContent = pomContent.substring(pomContent.indexOf("<artifactId>jweb-adai</artifactId>"));
            int start = pomContent.indexOf("<version>");
            int end = pomContent.indexOf("</version>");
            return pomContent.substring(start + 9, end);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static void cmd(String cmd, String workDirPath, Runnable runnable) {
        File dir = new File(workDirPath);
        if (!dir.exists()) {
            // workDirPath 不存在，将无法执行命令，所有这里就主动创建
            FileUtils.makeDir(workDirPath, true);
        }
        LogWebSocketService.sendMessage("执行命令：" + cmd);
        LogWebSocketService.sendMessage("执行目录：" + dir.getAbsolutePath());
        try {
            Process process = Runtime.getRuntime().exec(cmd, null, dir);
            Process finalProcess = process;
            //所有线程阻塞，然后统一开始
            CountDownLatch begin = new CountDownLatch(1);
            //主线程阻塞，直到所有分线程执行完毕
            CountDownLatch end = new CountDownLatch(2);
            begin.countDown();
            new Thread(() -> {
                InputStream is = finalProcess.getInputStream();
                InputStreamReader isr = new InputStreamReader(is, Charset.forName("UTF-8"));
                BufferedReader br = new BufferedReader(isr);
                try {
                    String content = br.readLine();
                    while (content != null) {
                        LogWebSocketService.sendMessage("1>：" + content);
                        content = br.readLine();
                    }
                } catch (IOException e) {
                    LogWebSocketService.sendMessage("1>：" + e.getMessage());
                }
                end.countDown();
            }).start();
            new Thread(() -> {
                InputStream is = finalProcess.getErrorStream();
                InputStreamReader isr = new InputStreamReader(is, Charset.forName("UTF-8"));
                BufferedReader br = new BufferedReader(isr);
                try {
                    String content = br.readLine();
                    while (content != null) {
                        LogWebSocketService.sendMessage("2>：" + content);
                        content = br.readLine();
                    }
                } catch (IOException e) {
                    LogWebSocketService.sendMessage("2>：" + e.getMessage());
                }
                end.countDown();
            }).start();
            //多个线程都执行结束
            end.await();
            if (runnable != null) {
                runnable.run();
            }
        } catch (Exception e) {
            logger.error("调用程序异常！", e);
            LogWebSocketService.sendError(e);
        }
    }

    private static void cmd(String cmd, String dir) {
        cmd(cmd, dir, null);
    }

    /**
     * 是否以jar运行
     *
     * @return
     */
    public static boolean isRunInJar() {
        return CodeGenerator.class.getClassLoader().getResource("").getPath().endsWith("jar!/");
    }

    /**
     * 获取fatjar的包位置
     *
     * @return
     */
    public static String getFatJarPath() {
        String path = CodeGenerator.class.getClassLoader().getResource("").getPath();
        if (path.endsWith("jar!/")) {
            path = new File(path.substring(6, path.length() - 2)).getAbsolutePath();
        }
        return path;
    }


    public static String getClassPathParams() {
        StringBuilder sb = new StringBuilder(" -cp \"");
        File rootClassPath = new File(PathKit.getRootClassPath());
        if (isRunInJar()) {
            sb.append(getFatJarPath()).append(";");
        }
        sb.append(PathKit.getRootClassPath() + ";");
        sb.append(rootClassPath.getParentFile().getAbsolutePath() + "/lib/*;");
        sb.append("\" ");
        return sb.toString();
    }

    public static String getTestWebClassPathParams(String targetClassesPath) {
        StringBuilder sb = new StringBuilder(" -cp \"");
        File rootClassPath = new File(PathKit.getRootClassPath());
        sb.append(targetClassesPath + ";");
        if (isRunInJar()) {
            sb.append(getFatJarPath()).append(";");
        }
        sb.append(PathKit.getRootClassPath() + ";");
        sb.append(rootClassPath.getParentFile().getAbsolutePath() + "/lib/*;");
        sb.append("\" ");
        return sb.toString();
    }

    private static StringBuilder generatorClassPathFileParam(File file) {
        StringBuilder sb = new StringBuilder();
        if (file.isDirectory()) {
            for (File listFile : file.listFiles()) {
                sb.append(generatorClassPathFileParam(listFile));
            }
        } else {
            sb.append(file.getAbsolutePath() + ";");
        }
        return sb;
    }

    private static StringBuilder generatorCompileSourceFiles(File file) {
        StringBuilder sb = new StringBuilder();
        if (file.isDirectory()) {
            for (File listFile : file.listFiles()) {
                sb.append(generatorCompileSourceFiles(listFile));
            }
        } else {
            if (file.getName().endsWith(".java")) {
                sb.append(" " + file.getAbsolutePath());
            }
        }
        return sb;
    }

    /**
     * 启动测试Web服务
     *
     * @param generatorModel
     * @param debug
     */
    public static int startTestWeb(GeneratorModel generatorModel, Boolean debug) throws Exception {
        // 1.拷贝web资源
        LogWebSocketService.sendMessage("正在准备测试WEB资源...");
        String codeOutputPath = getCodeOutputPath(generatorModel);
        String webappPath = codeOutputPath + "/target/classes/webapp";
        String webRootPath = PathKit.getWebRootPath();
        if (!FileUtils.fileExists(webappPath)) {
            FileUtils.makeDir(webappPath, true);
        }
//        String webappAssetsPath = webappPath + "/assets";
//        if (!FileUtils.fileExists(webappAssetsPath)) {
//            LogWebSocketService.sendMessage("正在复制assets资源...");
//            FileUtils.copy(webRootPath + "/assets", webappAssetsPath);
//        }
        LogWebSocketService.sendMessage("正在复制WEB-INF/views资源...");
        FileUtils.copy(codeOutputPath + "/src/main/webapp/WEB-INF/views", webappPath + "/WEB-INF/views");
//        LogWebSocketService.sendMessage("正在修改WEB菜单信息...");
        String menuPath = webappPath + "/assets/test_menu.json";
//        if (!FileUtils.fileExists(menuBakPath)) {
//            FileUtils.copy(menuPath, menuBakPath);
//        }
//        FileUtils.copy(menuBakPath, menuPath);
        String menuJsonString = "{" +
                "\"code\":0," +
                "\"msg\":\"\"," +
                "\"data\":[{ " +
                "\"name\":\"test\"," +
                "\"title\":\"" + generatorModel.getGeneratorName() + "-测试\"," +
                "\"icon\":\"layui-icon-rate-solid\"," +
                "\"jump\":\"" + generatorModel.toGeneratorConfig().getCtlUri() + "\"" +
                "}]" +
                "}";
        FileUtils.write(menuPath, menuJsonString, Charset.forName("UTF-8"));
//        // 初始化扩展类查找路径
        String extClassPath = codeOutputPath + "/target/classes"; // + webRootPath + "/WEB-INF/lib/;" + webRootPath + "/WEB-INF/classes/;";
        LogWebSocketService.sendMessage("正在启动WEB测试服务...");
        return WebServerService.startWebWithJwebBoot(extClassPath, extClassPath, debug);
    }

    /**
     * 启动测试Web服务
     *
     * @param tableModel
     */
    public static int startTestWeb(TableModel tableModel) throws Exception {
        // 1.拷贝web资源
        LogWebSocketService.sendMessage("正在准备测试WEB资源...");
        String codeOutputPath = getCodeOutputPath(tableModel);
        // 初始化扩展类查找路径
        String extClassPath = codeOutputPath + "/target/classes/";
        LogWebSocketService.sendMessage("正在启动WEB测试服务...");
        return WebServerService.startWebWithJwebBoot(extClassPath, extClassPath, false);
    }


    /**
     * 替换文件文本内容
     *
     * @param filePath
     * @param regex
     * @param replacement
     * @param isReplaceAll
     * @throws IOException
     */
    public static void replaceFileContent(String filePath, String regex, String replacement, boolean isReplaceAll, Charset charset) throws IOException {
        String content = FileUtils.readText(filePath, System.lineSeparator(), charset);
        if (isReplaceAll) {
            content = content.replaceAll(regex, replacement);
        } else {
            content = content.replaceFirst(regex, replacement);
        }

        FileUtils.write(filePath, content, charset);
    }

    /**
     * 停止测试Web服务
     *
     * @param port
     */
    public static void stopTestWeb(int port) throws Exception {
        WebServerService.stopWeb(port);
    }

    /**
     * 部署代码
     *
     * @param generatorModel
     */
    public static void deployCode(GeneratorModel generatorModel) throws Exception {
        String webRootPath = PathKit.getWebRootPath();
        String codeOutputPath = getCodeOutputPath(generatorModel);
        String classPath = PathKit.getRootClassPath();
        String projectPath = getProjectPath();
        LogWebSocketService.sendMessage("准备部署编译文件到" + classPath);
        String generatorClassPath = codeOutputPath + "/target/classes";
        if (FileUtils.fileExists(generatorClassPath)) {
            // 复制除webapp外的所有文件与目录
            File[] files = new File(generatorClassPath).listFiles((dir, name) -> !"webapp".equals(name));
            if (files != null) {
                for (File file : files) {
                    FileUtils.copy(file.getAbsolutePath(), classPath + File.separator + file.getName());
                }
            }
            LogWebSocketService.sendMessage("部署编译文件成功!");
        } else {
            LogWebSocketService.sendMessage("找不到生成代码的编译文件!");
        }
        // 部署源码
        String devPath = projectPath + "/src/main";
        if (new File(devPath).exists()) {
            LogWebSocketService.sendMessage("监测到开发环境目录，准备部署源码！");
            FileUtils.copy(codeOutputPath + "/src/main", devPath);
            LogWebSocketService.sendMessage("部署源码成功!");
        }
    }

    /**
     * 获取工程目录
     *
     * @return
     */
    private static String getProjectPath() {
        String rootClassPath = PathKit.getRootClassPath();
        String projectPath = rootClassPath;
        if (rootClassPath.endsWith("target" + File.separator + "classes")) {
            projectPath = new File(rootClassPath).getParentFile().getParentFile().getAbsolutePath();
        } else if (rootClassPath.endsWith("webapp" + File.separator + "WEB-INF" + File.separator + "classes")) {
            projectPath = new File(rootClassPath).getParentFile().getParentFile().getParentFile().getAbsolutePath();
        }
        return projectPath;
    }

    /**
     * 部署代码
     *
     * @param tableModel
     * @throws Exception
     */
    public static void deployCode(TableModel tableModel) throws Exception {
        String codeOutputPath = getCodeOutputPath(tableModel);
        String classesPath = PathKit.getRootClassPath();
        String projectPath = getProjectPath();
        LogWebSocketService.sendMessage("准备部署编译文件到" + classesPath);
        String generatorClassPath = codeOutputPath + "/target/classes";
        if (FileUtils.fileExists(generatorClassPath)) {
            // 复制除webapp外的所有文件与目录
            File[] files = new File(generatorClassPath).listFiles((dir, name) -> !"webapp".equals(name));
            if (files != null) {
                for (File file : files) {
                    FileUtils.copy(file.getAbsolutePath(), classesPath + File.separator + file.getName());
                }
            }
            LogWebSocketService.sendMessage("部署编译文件成功!");
        } else {
            LogWebSocketService.sendMessage("找不到生成代码的编译文件!");
        }
        // 部署源码
        String devPath = projectPath + "/src/main";
        if (new File(devPath).exists()) {
            LogWebSocketService.sendMessage("监测到开发环境目录，准备部署源码！");
            FileUtils.copy(codeOutputPath + "/src/main", devPath);
            LogWebSocketService.sendMessage("部署源码成功!");
        }
    }

    /**
     * 获取模板文件目录
     *
     * @param templateModel
     * @return
     */
    public static String getTemplateFilePath(TemplateModel templateModel) {
        return CodeGenerator.templateDir + File.separator + templateModel.getTemplateId();
    }

    /**
     * 获取表模型模板目录
     *
     * @return
     */
    public static String getModelTemplateFilePath() {
        return modelTemplateDir;
    }

    /**
     * 获取表模型模板输出目录
     *
     * @param tableModel
     * @return
     */
    public static String getModelTemplateOutputFilePath(TableModel tableModel) {
        return generatorResDir + "/output/tid_" + tableModel.getTableId();
    }

    /**
     * 获取模板输出目录
     *
     * @param generatorModel
     * @return
     */
    public static String getTemplateOutputFilePath(GeneratorModel generatorModel) {
        return templateDir + "/output/generator_" + generatorModel.getGeneratorId();
    }
}
